#pragma once

#include "glib/operations.h"
#include <cmath>
#include <iostream>
#include <xtd/bistream>
#include <xtd/bostream>

namespace g4 {

template <typename T>
class basic_vector;

typedef basic_vector<double> vector;
typedef basic_vector<float> vectorf;
typedef basic_vector<long double> vectorl;

template <typename T>
class basic_vector {
public:
    T x, y, z, t;
public:
	static basic_vector<T> const nill; //(0,0,0,0) vector
public:
	typedef T * iterator;
	typedef const T * const_iterator;
public:
	T * begin()
		{ return &x; }
	const T * begin() const
		{ return &x; }
	T * end()
		{ return &t + 1; }
	const T * end() const
		{ return &t + 1; }
public:
	basic_vector()
		{ }
    basic_vector(T _x, T _y, T _z, T _t) 
		: x(_x), y(_y), z(_z), t(_t) { }
	template <class U> 
	basic_vector(const basic_vector<U> & v) 
		: x(T(v.x)), y(T(v.y)), z(T(v.z)), t(T(v.t)) { }
	basic_vector & zero() 
		{ x = y = z = t = 0; return * this; } 
public:
    basic_vector & operator += (const basic_vector<T> & v)
        { x += v.x; y += v.y; z += v.z; t += v.t; return * this; }
    basic_vector & operator -= (const basic_vector<T> & v)
        { x -= v.x; y -= v.y; z -= v.z; t -= v.t; return * this; }
    basic_vector & operator *= (T a)
        { x *= a; y *= a; z *= a; t *= a; return * this; }
    basic_vector & operator /= (T a)
        { x /= a; y /= a; z /= a; t /= a; return * this; }
	bool operator < (const basic_vector<T> & v) const
		{ return x < v.x && y < v.y && z < v.z && t < v.t; }
	bool operator <= (const basic_vector<T> & v) const
		{ return x <= v.x && y <= v.y && z <= v.z && t <= v.t; }
	bool operator == (const basic_vector<T> & v) const
		{ return x == v.x && y == v.y && z == v.z && t == v.t; }
	bool operator != (const basic_vector<T> & v) const
		{ return x != v.x || y != v.y || z != v.z && t != v.t; }
	const T & operator ()(int index) const
		{ return * (&x + index); }
public:
	T & operator ()(int index)
		{ return * (&x + index); }
	static int size() 
		{ return 4; }
};

template <typename T>
basic_vector<T> const basic_vector<T>::nill(0, 0, 0, 0);

template <typename T>
inline basic_vector<T> operator + (const basic_vector<T> & a, const basic_vector<T> & b) {
	basic_vector<T> t(a);
    return t += b;
}

template <typename T>
inline basic_vector<T> operator - (const basic_vector<T> & a, const basic_vector<T> & b) {
	basic_vector<T> t(a);
    return t -= b;
}

template <typename T>
inline basic_vector<T> operator * (const basic_vector<T> & v, T a) {
	basic_vector<T> t(v);
    return t *= a;
}

template <typename T>
inline basic_vector<T> operator * (T a, const basic_vector<T> & v) {
	basic_vector<T> t(v);
    return t *= a;
}

template <typename T>
inline basic_vector<T> operator / (const basic_vector<T> & v, T a) {
	basic_vector<T> t(v);
    return t /= a;
}

template <typename T>
inline T scalar_product(const basic_vector<T> & v1, const basic_vector<T> & v2) 
	{ return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.t * v2.t; }

} // g4

template <typename T>
class squarer<g4::basic_vector<T> > {
	T s;
public:
	explicit squarer(const g4::basic_vector<T> & v) : s(scalar_product(v, v)) { }
	operator T () const { return s; }
};

template <typename T>
class absoluter<g4::basic_vector<T> > {
	T a;
public:
	explicit absoluter(const g4::basic_vector<T> & v) : a(::sqrt(sqr(v))) { }
	operator T () const { return a; }
};


template <typename T>
std::istream & operator >> (std::istream & in, g4::basic_vector<T> & v) {
	return in >> v.x >> v.y >> v.z >> v.t;
}

template <typename T>
std::ostream & operator << (std::ostream & out, const g4::basic_vector<T> & v) {
	return out << v.x << " " << v.y << " " << v.z << " " << v.t;
}

template <typename T>
xtd::bistream & operator >> (xtd::bistream & in, g4::basic_vector<T> & v) {
	return in.get(v);
}

template <typename T>
xtd::bostream & operator << (xtd::bostream & out, const g4::basic_vector<T> & v) {
	return out.put(v);
}
